﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Somnus.Objects
{
    class Objeto2D
    {
        #region atributos_privados

        /// <summary>
        /// Controla o tamanho horizontal do objeto, baseado em uma porcentagem; 
        /// 1 = tamanho original; 2 = dobro do tamanho; 0.5 = metade do tamanho; 
        /// NÃO USE ESSA VARIÁVEL, AO INVÉS, USE O METODO "ScaleX";
        /// </summary>
        private float scaleX;

        /// <summary>
        /// Controla o tamanho vertical do objeto, baseado em uma porcentagem;
        /// 1 = tamanho original; 2 = dobro do tamanho; 0.5 = metade do tamanho; 
        /// NÃO USE ESSA VARIÁVEL, AO INVÉS, USE O METODO "ScaleY";
        /// </summary>
        private float scaleY;

        /// <summary>
        /// Controla o tamanho horizontal do objeto, baseado em pixels; 
        /// NÃO USE ESSA VARIÁVEL, AO INVÉS, USE O METODO "Width";
        /// </summary>
        private float width;

        /// <summary>
        /// Controla o tamanho vertical do objeto, baseado em pixels; 
        /// NÃO USE ESSA VARIÁVEL, AO INVÉS, USE O METODO "Width";
        /// </summary>
        private float height;

        /// <summary>
        /// Guarda a posição do ponto de referência.
        /// </summary>
        private Vector2 posPontoReferencia;



        protected Rectangle sourceRectangle;

        #endregion

        #region atributos_protegidos

        /// <summary>
        /// Textura do Objeto
        /// </summary>
        protected Texture2D textura;

        /// <summary>
        /// Guarda o estato de uma enumeração; Controla o ponto de referência;
        /// </summary>
        public PontoDeReferencia pontoDeReferencia;

        #endregion

        #region atributos_públicos

        /// <summary>
        /// Posição Horizontal
        /// </summary>
        public float x;

        /// <summary>
        /// Posição Vertical
        /// </summary>
        public float y;

        /// <summary>
        /// Rotação do objeto (em Graus)
        /// </summary>
        public float rotation;

        /// <summary>
        /// Controla o estádo de visibilidade do Objeto
        /// </summary>
        public bool visible;

        /// <summary>
        /// Esta variábel serve para desenhar o retangulo de colisão
        /// </summary>
        public bool drawCollisionBounds;

        /// <summary>
        /// 
        /// </summary>
        public float depth;

        /// <summary>
        /// 
        /// </summary>
        public float alpha;

        /// <summary>
        /// 
        /// </summary>
        public Color color;

        public bool inverted;

        /// <summary>
        /// 
        /// </summary>
        public SpriteEffects spriteEffects;

        public Rectangle collisionBounds
        {
            get
            {
                switch (pontoDeReferencia)
                {
                    case PontoDeReferencia.SUPERIOR_ESQUERDO:

                        return new Rectangle((int)this.x, (int)this.y, (int)sourceRectangle.Width, (int)sourceRectangle.Height);

                    case PontoDeReferencia.SUPERIOR_DIREITO:

                        return new Rectangle((int)this.x - (int)sourceRectangle.Width, (int)this.y, (int)sourceRectangle.Width, (int)sourceRectangle.Height);

                    case PontoDeReferencia.INFERIOR_ESQUERDO:

                        return new Rectangle((int)this.x, (int)this.y - (int)sourceRectangle.Height, (int)sourceRectangle.Width, (int)sourceRectangle.Height);

                    case PontoDeReferencia.INFERIOR_DIREITO:

                        return new Rectangle((int)this.x - (int)sourceRectangle.Width, (int)this.y - (int)sourceRectangle.Height, (int)sourceRectangle.Width, (int)sourceRectangle.Height);

                    case PontoDeReferencia.CENTRO:
                        return new Rectangle((int)(this.x - (sourceRectangle.Width / 2)), (int)(this.y - (sourceRectangle.Height / 2)), (int)sourceRectangle.Width, (int)sourceRectangle.Height);


                }

                return new Rectangle((int)this.x, (int)this.y, (int)sourceRectangle.Width, (int)sourceRectangle.Height);
            }
        }

        /// <summary>
        /// Enumeração dos estados do ponto de referência
        /// </summary>
        public enum PontoDeReferencia
        {
            SUPERIOR_ESQUERDO,
            SUPERIOR_DIREITO,
            INFERIOR_ESQUERDO,
            INFERIOR_DIREITO,
            CENTRO
        };

        #endregion



        /// <summary>
        /// Função construtora
        /// </summary>
        /// <param name="textura"></param>
        public Objeto2D(Texture2D textura, Nullable<Rectangle> sourceRectangle, float depth)
        {
            spriteEffects = SpriteEffects.None;
            this.textura = textura;
            x = y = 0;
            scaleX = scaleY = 1;
            rotation = 0;
            alpha = 1;
            color = Color.White;
            inverted = false;
            visible = true;

            if (sourceRectangle == null)
            {
                if(textura != null)
                {   
                    this.sourceRectangle = new Rectangle(0, 0, textura.Width, textura.Height);
                }
            }
            else
            {
                this.sourceRectangle = (Rectangle)sourceRectangle;
            }

            setarPontoDeReferencia(PontoDeReferencia.CENTRO);

            this.depth = depth;

            calcularWidth();
            calcularHeight();
        }



        /// <summary>
        /// Esta função serve para setar o ponto de referência da imagem
        /// (que imagem?)
        /// </summary>
        /// <param name="pontoDeReferencia"></param>
        public void setarPontoDeReferencia(PontoDeReferencia pontoDeReferencia)
        {
            switch (pontoDeReferencia)
            {
                case PontoDeReferencia.SUPERIOR_ESQUERDO:
                    posPontoReferencia = new Vector2(sourceRectangle.X, sourceRectangle.Y);
                    break;

                case PontoDeReferencia.SUPERIOR_DIREITO:
                    posPontoReferencia = new Vector2(sourceRectangle.Width, sourceRectangle.Y);
                    break;

                case PontoDeReferencia.INFERIOR_ESQUERDO:
                    posPontoReferencia = new Vector2(sourceRectangle.X, sourceRectangle.Height);
                    break;

                case PontoDeReferencia.INFERIOR_DIREITO:
                    posPontoReferencia = new Vector2(sourceRectangle.Width, sourceRectangle.Height);
                    break;

                case PontoDeReferencia.CENTRO:
                    posPontoReferencia = new Vector2(sourceRectangle.Width / 2, sourceRectangle.Height / 2);
                    break;
            }

            this.pontoDeReferencia = pontoDeReferencia;
        }


        #region width, height e scales

        public float ScaleX
        {
            get { return scaleX; }
            set
            {
                scaleX = value;
                calcularWidth();
            }
        }

        public float ScaleY
        {
            get { return scaleY; }
            set
            {
                scaleY = value;
                calcularHeight();
            }
        }

        public float Width
        {
            get { return width; }
            set
            {
                width = value;
                calcularScaleX();
            }
        }

        public float Height
        {
            get { return height; }
            set
            {
                height = value;
                calcularScaleY();
            }
        }

        private void calcularScaleX()
        {
            ScaleX = width / sourceRectangle.Width;
        }

        private void calcularScaleY()
        {
            ScaleY = height / sourceRectangle.Height;
        }

        private void calcularWidth()
        {
            width = sourceRectangle.Width * scaleX;
            if (width < 0)
            {
                width *= -1;
            }
        }

        private void calcularHeight()
        {
            height = sourceRectangle.Height * scaleY;
            if (Height < 0)
            {
                height *= -1;
            }
        }

        #endregion



        public virtual void draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            if (visible)
            {
                
                spriteBatch.Draw
                (
                    textura,
                    new Vector2(x, y),
                    sourceRectangle,
                    Color.Multiply(color, alpha),
                    MathHelper.ToRadians(rotation),
                    posPontoReferencia,
                    new Vector2(scaleX, scaleY),
                    (inverted? SpriteEffects.FlipHorizontally : SpriteEffects.None),
                    depth
                );

                if (drawCollisionBounds)
                {
                    Texture2D dummyTexture = new Texture2D(Game1.getInstance().GraphicsDevice, 1, 1);
                    dummyTexture.SetData(new Color[] { Color.White });
                    
                    spriteBatch.Draw(dummyTexture, collisionBounds, Color.White);
                }
            }
        }

    }
}
